package com.howe.gateway.utils;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.howe.gateway.entity.DubboEndpointsConfig;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;
import java.util.*;

public class CustomLoadBalancerUtil {
    private static final String PERCENTAGE_SIGN = "%";
    private static final String DEFAULT_SCHEME = "http";
    private static final String DEFAULT_SECURE_SCHEME = "https";
    private static final Map<String, String> INSECURE_SCHEME_MAPPINGS = new HashMap();


    private CustomLoadBalancerUtil() {
        throw new IllegalStateException("Can't instantiate a utility class");
    }

    private static boolean containsEncodedParts(URI uri) {
        boolean encoded = uri.getRawQuery() != null && uri.getRawQuery().contains("%") || uri.getRawPath() != null && uri.getRawPath().contains("%") || uri.getRawFragment() != null && uri.getRawFragment().contains("%");
        if (encoded) {
            try {
                UriComponentsBuilder.fromUri(uri).build(true);
                return true;
            } catch (IllegalArgumentException var3) {
                return false;
            }
        } else {
            return false;
        }
    }

    private static int computePort(ServiceInstance serviceInstance, String scheme) {
        Map<String, String> metadata = serviceInstance.getMetadata();
        String endpoints = metadata.get("dubbo.endpoints");
        int port = -1;
        if (Objects.isNull(endpoints)) {
            port = serviceInstance.getPort();
            if (port < 0) {
                port = Objects.equals(scheme, "https") ? 443 : 80;
            }
        }else{
            List<String> endpointArr = JSONArray.parseArray(endpoints,String.class);
            for (String endpoint : endpointArr) {
                DubboEndpointsConfig endpointsConfig = JSONObject.parseObject(endpoint, DubboEndpointsConfig.class);
                String protocol = endpointsConfig.getProtocol();
                if ("rest".equals(protocol)) {
                    port = endpointsConfig.getPort();
                }
            }
        }
        if(port < 0){
            throw new IllegalArgumentException("Service Instance rest port cannot be null.");
        }
        return port;
    }

    public static URI reconstructURI(ServiceInstance serviceInstance, URI original) {
        if (serviceInstance == null) {
            throw new IllegalArgumentException("Service Instance cannot be null.");
        } else {
            return doReconstructURI(serviceInstance, original);
        }
    }

    private static URI doReconstructURI(ServiceInstance serviceInstance, URI original) {
        String host = serviceInstance.getHost();
        String scheme = Optional.ofNullable(serviceInstance.getScheme()).orElse(computeScheme(original, serviceInstance));
        int port = computePort(serviceInstance, scheme);
        if (Objects.equals(host, original.getHost()) && port == original.getPort() && Objects.equals(scheme, original.getScheme())) {
            return original;
        } else {
            boolean encoded = containsEncodedParts(original);
            return UriComponentsBuilder.fromUri(original).scheme(scheme).host(host).port(port).build(encoded).toUri();
        }
    }

    private static String computeScheme(URI original, ServiceInstance serviceInstance) {
        String originalOrDefault = Optional.ofNullable(original.getScheme()).orElse("http");
        return serviceInstance.isSecure() &&
                INSECURE_SCHEME_MAPPINGS.containsKey(originalOrDefault) ?
                INSECURE_SCHEME_MAPPINGS.get(originalOrDefault) : originalOrDefault;
    }

    static {
        INSECURE_SCHEME_MAPPINGS.put("http", "https");
        INSECURE_SCHEME_MAPPINGS.put("ws", "wss");
    }
}
